package com.bigfans.paymentservice.service.impl;

import com.alipay.api.response.AlipayTradePrecreateResponse;
import com.alipay.demo.trade.config.Configs;
import com.alipay.demo.trade.model.ExtendParams;
import com.alipay.demo.trade.model.builder.AlipayTradePrecreateRequestBuilder;
import com.alipay.demo.trade.model.result.AlipayF2FPrecreateResult;
import com.alipay.demo.trade.service.AlipayTradeService;
import com.alipay.demo.trade.service.impl.AlipayTradeServiceImpl;
import com.bigfans.framework.dao.BaseServiceImpl;
import com.bigfans.framework.event.ApplicationEventBus;
import com.bigfans.framework.plugins.FileStoragePlugin;
import com.bigfans.framework.plugins.UploadResult;
import com.bigfans.framework.redis.JedisTemplate;
import com.bigfans.framework.utils.ImageUtils;
import com.bigfans.framework.utils.StringHelper;
import com.bigfans.model.event.payment.OrderPaidEvent;
import com.bigfans.model.event.payment.OrderPayFailureEvent;
import com.bigfans.paymentservice.dao.PaymentDAO;
import com.bigfans.paymentservice.model.Order;
import com.bigfans.paymentservice.model.PayResult;
import com.bigfans.paymentservice.model.Payment;
import com.bigfans.paymentservice.model.entity.PaymentEntity;
import com.bigfans.paymentservice.service.PaymentService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.env.Environment;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.io.File;

/**
 * @author lichong 2015年4月5日下午6:34:35
 * @Description:支付服务
 */
@Service(PaymentServiceImpl.BEAN_NAME)
public class PaymentServiceImpl extends BaseServiceImpl<Payment> implements PaymentService {

    private Logger log = LoggerFactory.getLogger(PaymentServiceImpl.class);
    public static final String BEAN_NAME = "paymentService";

    // 支付宝当面付2.0服务
    private static AlipayTradeService tradeService;

    private PaymentDAO paymentDAO;
    @Autowired
    private Environment environment;
    @Autowired
    private FileStoragePlugin fileStoragePlugin;
    @Autowired
    private RedisService redisService;
    @Autowired
    private ApplicationEventBus applicationEventBus;

    @Autowired
    public PaymentServiceImpl(PaymentDAO paymentDAO) {
        super(paymentDAO);
        this.paymentDAO = paymentDAO;
        /**
         * 一定要在创建AlipayTradeService之前调用Configs.init()设置默认参数
         * Configs会读取classpath下的zfbinfo
         * .properties文件配置信息，如果找不到该文件则确认该文件是否在classpath目录
         */
        Configs.init("props/zfbinfo.properties");

        /**
         * 使用Configs提供的默认参数 AlipayTradeService可以使用单例或者为静态成员对象，不需要反复new
         */
        tradeService = new AlipayTradeServiceImpl.ClientBuilder().build();

    }

    @Override
    public void create(Payment payment) throws Exception {
        super.create(payment);
        if (payment.getStatus() == PaymentEntity.STATUS_PAY_SUCCESS) {
            applicationEventBus.publishEvent(new OrderPaidEvent(payment.getOrderId()));
        } else if (payment.getStatus() == PaymentEntity.STATUS_PAY_FAILURE) {
            applicationEventBus.publishEvent(new OrderPayFailureEvent(payment.getOrderId()));
        }

    }

    public String generateF2FAlipayQrImage(Order order) throws Exception {
        String tmpQrImg = redisService.getOrderQrImg(order.getId());
        if (StringHelper.isNotEmpty(tmpQrImg)) {
            return tmpQrImg;
        }
        // (必填) 商户网站订单系统中唯一订单号，64个字符以内，只能包含字母、数字、下划线，
        // 需保证商户系统端不能重复，建议通过数据库sequence生成，
        String outTradeNo = order.getId();

        // (必填) 订单标题，粗略描述用户的支付目的。如“xxx品牌xxx门店当面付扫码消费”
        String subject = "bigfans当面付扫码消费";

        // (必填) 订单总金额，单位为元，不能超过1亿元
        // 如果同时传入了【打折金额】,【不可打折金额】,【订单总金额】三者,则必须满足如下条件:【订单总金额】=【打折金额】+【不可打折金额】
        String totalAmount = order.getTotalPrice().toString();

        // (可选) 订单不可打折金额，可以配合商家平台配置折扣活动，如果酒水不参与打折，则将对应金额填写至此字段
        // 如果该值未传入,但传入了【订单总金额】,【打折金额】,则该值默认为【订单总金额】-【打折金额】
        String undiscountableAmount = "0";

        // 卖家支付宝账号ID，用于支持一个签约账号下支持打款到不同的收款账号，(打款到sellerId对应的支付宝账号)
        // 如果该字段为空，则默认为与支付宝签约的商户的PID，也就是appid对应的PID
        String sellerId = "";

        // 商户操作员编号，添加此参数可以为商户操作员做销售统计
        String operatorId = "test_operator_id";

        // (必填) 商户门店编号，通过门店号和商家后台可以配置精准到门店的折扣信息，详询支付宝技术支持
        String storeId = "test_store_id";

        // 业务扩展参数，目前可添加由支付宝分配的系统商编号(通过setSysServiceProviderId方法)，详情请咨询支付宝技术支持
        ExtendParams extendParams = new ExtendParams();
        extendParams.setSysServiceProviderId("2088100200300400500");

        // 支付超时，定义为120分钟
        String timeoutExpress = "20m";

        // 商品明细列表，需填写购买商品详细信息，
//		List<GoodsDetail> goodsDetailList = new ArrayList<>();
//		List<OrderItem> items = payment.getOrderItemList();
//		for (OrderItem item : items) {
//			// 创建一个商品信息，参数含义分别为商品id（使用国标）、名称、单价（单位为分）、数量，如果需要添加商品类别，详见GoodsDetail
//			// 创建一个商品信息，参数含义分别为商品id（使用国标）、名称、单价（单位为分）、数量，如果需要添加商品类别，详见GoodsDetail
//			GoodsDetail goods1 = GoodsDetail.newInstance(item.getId(), item.getProdName(), item.getDealPrice()
//					.longValue(), item.getQuantity());
//			// 创建好一个商品后添加至商品明细列表
//			goodsDetailList.add(goods1);
//		}

        // 订单描述，可以对交易或商品进行一个详细地描述，比如填写"购买商品2件共15.00元"
        // String body = "购买商品2件共15.00元";
        String body = "购买商品" + order.getProdTotalQuantity() + "件共" + order.getTotalPrice().toString() + "元";

        // 创建扫码支付请求builder，设置请求参数
        AlipayTradePrecreateRequestBuilder builder = new AlipayTradePrecreateRequestBuilder().setSubject(subject)
                .setTotalAmount(totalAmount).setOutTradeNo(outTradeNo).setUndiscountableAmount(undiscountableAmount)
                .setSellerId(sellerId).setBody(body).setOperatorId(operatorId).setStoreId(storeId)
                .setExtendParams(extendParams).setTimeoutExpress(timeoutExpress)
                .setNotifyUrl(environment.getProperty("alipay.notify_url"));//支付宝服务器主动通知商户服务器里指定的页面http路径,根据需要设置
//				.setGoodsDetailList(goodsDetailList);

        AlipayF2FPrecreateResult aliResult = tradeService.tradePrecreate(builder);

        PayResult result = new PayResult();
        switch (aliResult.getTradeStatus()) {
            case SUCCESS:
                log.debug("支付宝预下单成功: )");
                AlipayTradePrecreateResponse response = aliResult.getResponse();
                result.setStatus(PayResult.PayStatus.SUCCESS);
                result.setTradeNo(response.getOutTradeNo());
                result.setQrCode(response.getQrCode());

                String qrCode = result.getQrCode();
                String forder = System.getProperty("user.dir") + File.separator + "temp";
                File forderFile = new File(forder);
                if (!forderFile.exists()) {
                    forderFile.mkdirs();
                }
                String filePath = String.format(forderFile + "/qr-%s.png", response.getOutTradeNo());
                File qrCodeImge = ImageUtils.getQRCodeImge(qrCode, 256, filePath);
                try {
                    String targetPath = "/qrImage/" + order.getId() + "/" + qrCodeImge.getName();
                    UploadResult res = fileStoragePlugin.upload(qrCodeImge, targetPath);
                    result.setQrImgPath(res.getFilePath());
                    redisService.setOrderQrImg(order.getId(), res.getFilePath());
                } catch (Exception e) {
                    // ignore
                } finally {
                    if (qrCodeImge != null && qrCodeImge.exists()) {
                        qrCodeImge.delete();
                    }
                }
                break;

            case FAILED:
                result.setStatus(PayResult.PayStatus.FAILED);
                log.debug("支付宝预下单失败!!!");
                break;

            case UNKNOWN:
                result.setStatus(PayResult.PayStatus.UNKNOWN);
                log.debug("系统异常，预下单状态未知!!!");
                break;

            default:
                log.debug("不支持的交易状态，交易返回异常!!!");
                break;
        }
        return result.getQrImgPath();
    }

    @Override
    public Payment getByTradeNo(String tradeNo) throws Exception {
        return paymentDAO.getByTradeNo(tradeNo);
    }

    @Override
    public Payment getByOrder(String uid, String orderId) throws Exception {
        return paymentDAO.getByOrder(uid, orderId);
    }

    @Override
    @Transactional
    public int updateStatusToPaid(String paymentId) throws Exception {
        applicationEventBus.publishEvent(new OrderPaidEvent(paymentId));
        Payment payment = new Payment();
        payment.setId(paymentId);
        payment.setStatus(Payment.STATUS_PAY_SUCCESS);
        return super.update(payment);
    }
}
